package com.tool.shiro.util;


import com.tool.shiro.annotation.ApiInfoAnnotation;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 无法分类工具方法统一放在此类中
 */
@Component
public class CommonUtils implements SmartInitializingSingleton {

    /**
     * 获取spring上下文
     */
    @Autowired
    private ApplicationContext applicationContextUtil;
    @Autowired
    private static ApplicationContext applicationContext;
    @Override
    public void afterSingletonsInstantiated() {
        applicationContext = applicationContextUtil;
    }

    /**
     * Map<String, Object> 类型转为实体类类型
     * @param map       转换对象
     * @param cls       实体类
     * @return          转换后的实体类
     */
    public static Object getDtoObject(Map<String, Object> map, Class<?> cls) throws Exception {
        Constructor<?> ct = cls.getConstructor();
        Object retobj = ct.newInstance();
        Class<?> supClass = cls.getSuperclass();
        Method[] ms = cls.getDeclaredMethods();
        if (!"java.lang.Object".equals(supClass.getName())) {
            Method[] supms = supClass.getDeclaredMethods();
            ms = ArrayUtils.addAll(ms, supms);
        }
        for (int i = 0; i < ms.length; i++) {
            Method m = ms[i];
            Class<?> pts[] = m.getParameterTypes();
            if (m.getReturnType().toString().equals("void")) {
                Object value = map.get(m.getName().replaceAll("set", "").toUpperCase());
                if (value != null) {
                    if (pts[0] == String.class) {
                        m.invoke(retobj, new Object[] { value.toString() });
                    } else if (pts[0] == Integer.class||pts[0] == int.class) {
                        m.invoke(retobj, new Object[] { Integer.parseInt(value.toString()) });
                    } else if (pts[0] == Long.class||pts[0] == long.class) {
                        m.invoke(retobj, new Object[] { Long.parseLong(value.toString()) });
                    } else if (pts[0] == Double.class||pts[0] == double.class) {
                        m.invoke(retobj, new Object[] { Double.parseDouble(value.toString()) });
                    } else if (pts[0] == Calendar.class) {
                        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(format.parse(value.toString()));
                        m.invoke(retobj, new Object[] { calendar });
                    }else if (pts[0] == BigDecimal.class) {
                        m.invoke(retobj, new Object[] { value });
                    }
                } else {
                    if (pts[0] == Integer.class) {
                        m.invoke(retobj, new Object[] { 0 });
                    } else if (pts[0] == Long.class) {
                        m.invoke(retobj, new Object[] { 0L });
                    } else if (pts[0] == Double.class) {
                        m.invoke(retobj, new Object[] { 0.00 });
                    }
                }
            }
        }
        return retobj;
    }

    /**
     * 循环目标列表字段和源列表,将关键字段相同的值合并
     * @param targetList    目标列表
     * @param sourceList    源列表
     * @param key           关键字段
     * @return              合并后的列表
     */
    public static List<Map<String, Object>> mergeListBySpecifyField(List<Map<String, Object>> targetList, List<Map<String, Object>> sourceList, String key) {
        List<Map<String, Object>> result = new ArrayList<>();
        // 循环目标列表
        for (int i = 0 ; i < targetList.size() ; i++) {
            // 循环源列表
            for (int j = 0 ; j < sourceList.size() ; j++) {
                // 如果目标列表和源列表中关键字一样
                if (targetList.get(i).get(key).toString().equals(sourceList.get(j).get(key).toString())) {
                    // TODO: 这里默认知道List中Map的字段名,且targetList为ApiInfoAnnotation,sourceList为RequestMapping
                    //  后续进行扩展修改,只需要将两个List和关键字段传进来自动解析两个List中Map的字段名称进行添加
                    Map<String, Object> map = new HashMap<>();
                    map.put("url", sourceList.get(j).get("url"));
                    map.put("method", sourceList.get(j).get("method"));
                    map.put("class", sourceList.get(j).get("class"));
                    map.put("description", targetList.get(i).get("description"));
                    result.add(map);
                    break;
                }
            }
        }
        return result;
    }

    // TODO: 后期抽象，获取指定注解所有参数值
    // 资源表中需添加@RequiresRoles和@RequiresPermissions注解信息
    /**
     * 通过包地址获取获取指定注解中的指定字段
     * @param annotationClass       指定注解类
     * @param annotationField       指定字段
     * @param className             包地址
     * @throws ClassNotFoundException   *
     * @throws NoSuchMethodException    *
     * @throws InvocationTargetException    *
     * @throws IllegalAccessException   *
     */
    public static Map<String, Object> getAnnotationInfo(Class<ApiInfoAnnotation> annotationClass, String annotationField, String className) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method[] methods = Class.forName(className).getDeclaredMethods();
        Map<String, Object> map = new HashMap<>();
        for (Method m : methods){
            if(m.isAnnotationPresent(annotationClass)){
                Annotation an = m.getAnnotation(annotationClass);
                Method anMethod = an.getClass().getDeclaredMethod(annotationField, null);
                Object object = anMethod.invoke(an,  null);
                map.put(className + "." + m.getName(), object.toString());
            }
        }
        return map;
    }

    /**
     * 获取RequestMapping注解信息
     * @return      *
     */
    public static List<Map<String, Object>> getAllRequestMappingApiInfo() {
        RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        // 获取所有 @RequestMapping 注解方法
        List<Map<String, Object>> requestMappingList = new ArrayList<>();
        Map<RequestMappingInfo, HandlerMethod> map = handlerMapping.getHandlerMethods();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodEntry : map.entrySet()) {
            Map.Entry<RequestMappingInfo, HandlerMethod> entry = requestMappingInfoHandlerMethodEntry;
            // 如果不是BasicErrorController中的方法
            if (!entry.getValue().getBean().toString().contains("basicErrorController")) {
                // 存储获得的所有URL方法
                Map<String, Object> map1 = new HashMap<>();
                map1.put("url", StringUtils.join(entry.getKey().getPathPatternsCondition().getPatterns().toArray(), ","));       // URL
                map1.put("method", (entry.getKey().getMethodsCondition().isEmpty() ? "GET,POST" : StringUtils.join(entry.getKey().getMethodsCondition().getMethods().toArray(), ",")));         // methods
                map1.put("class", entry.getValue().toString().split("\\(")[0].replace("#", "."));
                requestMappingList.add(map1);
            }
        }
        return requestMappingList;
    }
}
